package com.sy.olddriver.servlet;

import com.alibaba.fastjson.JSON;
import com.sy.olddriver.annotation.ResponseBody;
import com.sy.olddriver.constant.AppConstant;
import com.sy.olddriver.dto.HandlerMethod;
import com.sy.olddriver.dto.JsonResult;
import com.sy.olddriver.exception.BusinessException;

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@WebServlet(urlPatterns = "/", loadOnStartup = 1)
//标记当前Servlet支持文件上传功能
@MultipartConfig
public class DispatcherServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        //获取请求资源路径
        String uri = req.getRequestURI();
        //获取映射信息
        Map<String, HandlerMethod> urlMappings = (Map<String, HandlerMethod>) req.getSession().getServletContext().getAttribute(AppConstant.URL_MAPPINGS);
        //获取处理器及处理器方法信息
        HandlerMethod handlerMethod = urlMappings.get(uri);
        JsonResult jsonResult = new JsonResult();
        res.setContentType("application/json;charset=utf-8");
        PrintWriter pw = res.getWriter();
        //如果没有找到处理器及方法信息
        if (handlerMethod == null) {
            jsonResult.setCode(404);
            res.setStatus(404);
            jsonResult.setError("请求资源不存在");
        }
        //如果能找到处理器及方法信息，则反射调用处理器方法
        else {
            //获取模型对象实例
            Object modelObj = handlerMethod.getModelObj();
            //获取要调用的方法
            Method method = handlerMethod.getMethod();
            try {
                //允许模型方法中灵活加入HttpServletRequest、HttpServletResponse对象
                //1.获取方法中的参数类型
                Parameter[] params = method.getParameters();
                //2.创建用于存放参数信息的集合
                List<Object> paramValueList = new ArrayList<>();
                //3.根据方法中参数类型，对集合元素进行填充
                for (Parameter param : params) {
                    //获取参数类型
                    Class<?> paramType = param.getType();
                    if (paramType == HttpServletRequest.class) {
                        paramValueList.add(req);
                    }
                    if (paramType == HttpServletResponse.class) {
                        paramValueList.add(res);
                    }
                    //TODO:其它参数类型可以在这里补充
                }
                //反射调用模型方法
                Object result = method.invoke(modelObj, paramValueList.toArray());
                //获取方法的返回值类型
                Class<?> returnType = method.getReturnType();
                //获取方法是否还有@ResponseBody注解
                boolean isAnnotatedByResponseBody = method.isAnnotationPresent(ResponseBody.class);
                //1.如果含有@ResponseBody注解，则直接以JSON返回
                if (isAnnotatedByResponseBody) {
                    res.setStatus(200);
                    jsonResult.setCode(200);
                    jsonResult.setData(result);
                }
            } catch (Exception e) {
                jsonResult.setCode(500);
                res.setStatus(500);
                //如果是业务异常
                if (e.getCause() instanceof BusinessException) {
                    jsonResult.setError(e.getCause().getMessage());
                } else {
                    e.printStackTrace();
                    jsonResult.setError("服务端异常");
                }

            }
        }
        pw.print(JSON.toJSONString(jsonResult,true));
    }
}
